
#include "MemoryBlock.h"

#define MBL_DATA_OFFSET(type, member) (unsigned long)&(((type *)0)->member)

PMEMORY_BLOCK_LIST InitMemoryBlockList(ULONG uSize, ULONG uCount, BOOL bSupper)
{
    if (0 == uSize || 0 == uCount)
    {
        return NULL;
    }
    ULONG uLenght = sizeof(MEMORY_BLOCK_LIST) + (uSize + sizeof(DATA_BLOCK)) * uCount;
    if (uLenght > 1024 * 1024 * 1024)
    {
        return NULL;
    }
    PMEMORY_BLOCK_LIST pBlockList = (PMEMORY_BLOCK_LIST)malloc(uLenght);
    
    if (NULL == pBlockList)
    {
        return NULL;
    }
    memset(pBlockList, 0, uLenght);

    DATA_BLOCK* First = (DATA_BLOCK*)((char*)pBlockList + sizeof(MEMORY_BLOCK_LIST));
    pBlockList->dwFlags = bSupper ? MBL_ALLOC_TEMP : 0;
    pBlockList->uSize = uSize;
    pBlockList->uCount = uCount;
    pBlockList->First = First;

    uCount -= 1;
    for (int i = 0; uCount > i; i++)
    {
        First->Next = (DATA_BLOCK*)((char*)First + (uSize + sizeof(DATA_BLOCK)));
        First = First->Next;
    }

    return pBlockList;
}

PVOID AllocMemoryBlock(PMEMORY_BLOCK_LIST pBlockList)
{
    DATA_BLOCK* pBlock = NULL;

    while (InterlockedCompareExchange(&pBlockList->Locked, TRUE, FALSE) == TRUE);

    if (pBlockList->First)
    {
        pBlock = pBlockList->First;
        pBlock->Next = NULL;
        pBlockList->First = pBlockList->First->Next;
        pBlockList->uCurrent++;
    }

    InterlockedExchange(&pBlockList->Locked, FALSE);

    if (NULL == pBlock || MBL_ALLOC_TEMP & pBlockList->dwFlags)
    {
        pBlock = (DATA_BLOCK*)malloc(pBlockList->uSize + sizeof(DATA_BLOCK));
        if (pBlock)
        {
            pBlock->Next = NULL;
            pBlock->dwFlags = DBTEMP;
            InterlockedIncrement(&pBlockList->uTempCount);
        }
    }

    return pBlock ? pBlock->Buffer : NULL;
}

void FreeMemoryBlock(PMEMORY_BLOCK_LIST pBlockList, PVOID pBuffer)
{
    DATA_BLOCK* pBlock = (DATA_BLOCK *)((char*)pBuffer - MBL_DATA_OFFSET(DATA_BLOCK, Buffer));

    if (DBTEMP & pBlock->dwFlags)
    {
        free(pBlock);
        InterlockedDecrement(&pBlockList->uTempCount);
    }
    else
    {
        while (InterlockedCompareExchange(&pBlockList->Locked, TRUE, FALSE) == TRUE);

        pBlock->Next = pBlockList->First;
        pBlockList->First = pBlock;
        pBlockList->uCurrent--;
        InterlockedExchange(&pBlockList->Locked, FALSE);
    }
}

void ReleaseMemoryBlockList(PMEMORY_BLOCK_LIST pBlockList)
{
    free(pBlockList);
}
